/*
 * Copyright 2024 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.gitee.joyz.api.result.i18n;

import io.gitee.joyz.api.result.i18n.exception.NoSuchLocaleException;
import io.gitee.joyz.api.result.i18n.exception.NoSuchMessageException;
import io.gitee.joyz.api.result.util.ObjectUtils;

import java.text.MessageFormat;
import java.util.Locale;

/**
 * 该抽象类实现了 {@link LocaleMessage}, {@link HierarchicalLocaleMessage} 接口, 继承了 {@link MessageFormatSupport} 抽象类.
 * 对其接口中提供的 getMessage 方法进行了实现.
 * <p> 其实现类必须重写 resolveMessage 方法
 *
 * @author JoyZ
 */
public abstract class AbstractLocaleMessage extends MessageFormatSupport implements HierarchicalLocaleMessage {

    private Locale defaultLocale;


    private boolean fallbackToSystemLocale = false;


    private boolean useCodeAsDefaultMessage = false;


    private LocaleMessage parentLocaleMessage;


    @Override
    public String getMessage(String code, Object[] arguments, String defaultMessage, Locale locale) {

        String message = resolveInternal(code, arguments, locale);

        if (message != null) {
            return message;
        }

        if (defaultMessage == null) {

            String fallback = getDefaultMessage(code);

            if (fallback != null) {
                return fallback;
            }
        }

        return format(defaultMessage, arguments, locale);
    }

    @Override
    public String getMessage(String code, Object[] arguments, Locale locale) throws NoSuchMessageException {
        String message = resolveInternal(code, arguments, locale);

        if (message != null) {
            return message;
        }

        String fallback = getDefaultMessage(code);

        if (fallback != null) {
            return fallback;
        }

        throw new NoSuchMessageException(code, locale);
    }

    /**
     * 尝试根据给定的 code 解析消息.
     *
     * @param code      消息代码
     * @param arguments 消息文本参数
     * @param locale    语言环境
     * @return 解析后的消息文本, 如果没有找到则返回 null
     */
    protected String resolveInternal(String code, Object[] arguments, Locale locale) {

        if (code == null) {
            return null;
        }

        if (locale == null) {
            locale = getDefaultLocale();

            if (locale == null) {
                throw new NoSuchLocaleException();
            }
        }

        if (ObjectUtils.isEmpty(arguments)) {

            String message = resolveWithoutArguments(code, locale);

            if (message != null) {
                return message;
            }
        } else {

            Object[] resolvedArguments = resolveArguments(arguments);

            MessageFormat messageFormat = resolveMessage(code, locale);

            if (messageFormat != null) {
                synchronized (messageFormat) {
                    return messageFormat.format(resolveArguments(resolvedArguments));
                }
            }
        }

        // 如果消息为解析成功, 则委托给其设置的父 LocaleMessage 实例进行解析
        return getMessageFromParent(code, arguments, locale);
    }

    /**
     * 解析无参数的消息.
     *
     * @param code   消息代码
     * @param locale 语言环境
     * @return 解析后的消息, 如果未找到消息, 则返回 {@code null}
     */
    protected String resolveWithoutArguments(String code, Locale locale) {
        MessageFormat messageFormat = resolveMessage(code, locale);

        if (messageFormat != null) {
            synchronized (messageFormat) {
                return messageFormat.format(resolveArguments(null));
            }
        }

        return null;
    }

    /**
     * 尝试通过父组件解析消息.
     *
     * @param code      消息代码
     * @param arguments 消息参数
     * @param locale    语言环境
     * @return 如果解析成功, 则返回消息, 否则返回 {@code null}
     */
    protected String getMessageFromParent(String code, Object[] arguments, Locale locale) {
        LocaleMessage parent = getParent();

        if (parent == null) {
            return null;
        }

        if (parent instanceof AbstractLocaleMessage) {
            return ((AbstractLocaleMessage) parent).resolveInternal(code, arguments, locale);

        } else {
            return parent.getMessage(code, arguments, null, locale);
        }
    }

    /**
     * 设置默认语言环境.
     * 在进行消息解析时, 若未传入 Locale 实例, 系统则会来获取设置的默认语言环境
     *
     * @param defaultLocale Locale 实例
     */
    public void setDefaultLocale(Locale defaultLocale) {
        this.defaultLocale = defaultLocale;
    }

    /**
     * 设置是否允许回退至系统默认语言环境.
     * <p>在进行消息解析时, 当遇到未传入 Locale 实例且也未配置 defaultLocale 的情况下,
     * 系统会根据该配置决定是否使用系统默认语言环境.
     * <p><b>注意:</b> 在 Web 环境中, 服务器和客户端的语言环境有所差异, 所以请谨慎使用此配置
     * <p><b>Default:</b> false
     *
     * @param fallbackToSystemLocale boolean 类型参数
     */
    public void setFallbackToSystemLocale(boolean fallbackToSystemLocale) {
        this.fallbackToSystemLocale = fallbackToSystemLocale;
    }

    /**
     * 设置当消息无法找到时, 是否允许使用消息代码作为默认消息返回
     * <p><b>Default:</b> false
     *
     * @param useCodeAsDefaultMessage boolean 类型参数
     */
    public void setUseCodeAsDefaultMessage(boolean useCodeAsDefaultMessage) {
        this.useCodeAsDefaultMessage = useCodeAsDefaultMessage;
    }

    @Override
    public void setParent(LocaleMessage parent) {
        if (parent.equals(this)) {
            throw new IllegalArgumentException("Cannot set the instance itself as the parent component");
        }
        this.parentLocaleMessage = parent;
    }

    /**
     * 获取语言默认环境 Locale 实例.
     * 该方法默认实现为当解析消息时, 在未传入 Locale 实例且也未配置 defaultLocale 的情况下,
     * 系统会根据该配置决定是否使用系统默认语言环境
     *
     * @return boolean 类型返回值
     */
    public Locale getDefaultLocale() {
        if (defaultLocale != null) {
            return defaultLocale;
        }

        if (fallbackToSystemLocale) {
            return Locale.getDefault();
        }

        return null;
    }

    /**
     * 通过策略获取默认消息.
     *
     * @param code 消息代码
     * @return 如果 {@link #isUseCodeAsDefaultMessage} 返回 true, 则返回消息代码, 否则返回 null
     * @see #isUseCodeAsDefaultMessage()
     */
    protected String getDefaultMessage(String code) {
        if (isUseCodeAsDefaultMessage()) {
            return code;
        }
        return null;
    }


    /**
     * 当消息无法找到时, 是否允许使用消息代码作为默认消息返回.
     *
     * @return boolean 类型返回值
     */
    protected boolean isUseCodeAsDefaultMessage() {
        return useCodeAsDefaultMessage;
    }

    @Override
    public LocaleMessage getParent() {
        return parentLocaleMessage;
    }

    /**
     * 解析消息, 返回一个 {@link MessageFormat} 实例.
     *
     * @param code   消息代码
     * @param locale 语言环境
     * @return 返回解析成功后的消息, 如果未找到则返回 null
     */
    protected abstract MessageFormat resolveMessage(String code, Locale locale);
}
